package com.simons.common.utils.xml;

import java.io.File;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Source;
import javax.xml.transform.sax.SAXSource;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

import com.simons.common.exception.CommonErrorType;
import com.simons.common.exception.CommonException;

import lombok.extern.log4j.Log4j2;

@Log4j2
public class XmlUtils {
	
	/**
	 * 一个xml字符串转对象，忽略xml命名空间
	 * @param xml
	 * @param msgVo 根元素实体
	 * @return
	 * @throws CommonException 
	 */
	public static <T> T xmlToBean(String xml, Class<T> clazz) throws CommonException {
		if (clazz == null) {
			return null;
		}
		try{
			JAXBContext context = JAXBContext.newInstance(clazz);
			Unmarshaller unmarshaller = context.createUnmarshaller();
			Source source = trunSource(xml);
			JAXBElement<T> jaxbElement =  unmarshaller.unmarshal(source, clazz);
			return (T) jaxbElement.getValue();
		}catch(JAXBException e){
			log.error("", e);
			throw new CommonException(CommonErrorType.COMMON_XML_BEAN_ERROR);
		}catch (SAXException e){
			log.error("", e);
			throw new CommonException(CommonErrorType.COMMON_XML_BEAN_ERROR);
		}catch (ParserConfigurationException e){
			log.error("", e);
			throw new CommonException(CommonErrorType.COMMON_XML_BEAN_ERROR);
		}
	}
	
    /**
     * xml字符串集合转对象，忽略xml命名空间
     * @param xml xml字符串集合
     * @param msgVo 根元素实体
     */
	@SuppressWarnings("unchecked")
	public static  <T> List<T> xmlToBean(List<String> xmlList,Class<T> clazz)throws JAXBException,SAXException,ParserConfigurationException {
		if (clazz==null) {
			return Collections.EMPTY_LIST;
		}		
		List<T> beanList = new LinkedList<T>();
		JAXBContext context = JAXBContext.newInstance(clazz);		 
        Unmarshaller unmarshaller = context.createUnmarshaller();
        for (String xmlStr : xmlList) {
             Source source = trunSource(xmlStr);
             beanList.add((T)unmarshaller.unmarshal(source));     		
		}
        return  beanList;
	}
	
	public static String bean2Xml(Object obj) throws CommonException {
		return bean2Xml(obj, "UTF-8");
	}

	public static String bean2Xml(Object obj, String encoding) throws CommonException  {
		try{
			JAXBContext context = JAXBContext.newInstance(obj.getClass());
			Marshaller marshaller = context.createMarshaller();
			marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
			marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);
	
			StringWriter writer = new StringWriter();
			marshaller.marshal(obj, writer);
			return writer.toString();
		}catch (JAXBException e){
			log.error("", e);
			throw new CommonException(CommonErrorType.COMMON_BEAN_XML_ERROR);
		}
	}
	
	private static Source trunSource(String xmlStr) throws SAXException,ParserConfigurationException {
		 StringReader reader = new StringReader(xmlStr); 
	     SAXParserFactory sax = SAXParserFactory.newInstance();  
         sax.setNamespaceAware(false);  
         XMLReader xmlReader = sax.newSAXParser().getXMLReader();
         Source source = new SAXSource(xmlReader, new InputSource(reader));  
		 return source;		
	}
	
	@SuppressWarnings("unchecked")
	public static ElementBean transElement2Map(Element ele){  
		if(ele == null){
			return null;
		}
		
        ElementBean elementBean = new ElementBean();
        elementBean.setName(ele.getName());
    	elementBean.setAttributeMap( getAttributeMap(ele) );
    	
        List<Element> list = ele.elements();  
        if(list.isEmpty()){
        	return elementBean;
        }
        
        List<ElementBean> subElementList = new ArrayList<ElementBean>();
        for(Element item : list){
        	ElementBean subElement = transElement2Map(item);
        	subElementList.add(subElement);
        }
        
        elementBean.setSubElement(subElementList);
        return elementBean;
    }  
	
	@SuppressWarnings("unchecked")
	private static Map<String, String> getAttributeMap(Element ele){
		Map<String,String> attributeMap = new HashMap<String,String>();
		List<Attribute> list = ele.attributes();
		for(Attribute attr: list){
			attributeMap.put(attr.getName(), attr.getValue());
		}
		
		return attributeMap;
	}
	
	
	//新增余国
	public static Map<String, Object> xmlFile2Map(String filePath) throws DocumentException {

		File f = new File(filePath);
		SAXReader reader = new SAXReader();

		Document doc = reader.read(f);

		return doc2Map(doc);
	}
	
	public static Map<String,Object> xmlStr2Map(String xmlStr) throws DocumentException{
		
		StringReader str = new StringReader(xmlStr);
		
		SAXReader reader = new SAXReader();
		
		Document doc = reader.read(str);
		
		return doc2Map(doc);
	}
	
	static Map<String,Object> doc2Map(Document doc){
		
		Element root = doc.getRootElement();

		Map<String, Object> result = iterateElement2Map(root);

		return result;
	}
	
	@SuppressWarnings("unchecked")
	static Map<String,Object> iterateElement2Map(Element e){
		
		Map<String,Object> map = new HashMap<String,Object>();
		
		List<Element> list = e.elements();
		
		if(list.size() > 0){
			List<Map<String,Object>> listE = new ArrayList<Map<String,Object>>();
			for(Element item:list){
				listE.add(iterateElement2Map(item));
			}
			
			map.put(e.getName(), listE);
			
		}else{
			map.put(e.getName(), e.getText());
		}
		
		return map;
	}
	
}
